#ifndef _FLOWSIMGPU_HEADER_
#define _FLOWSIMGPU_HEADER_
#include <memory>
#include <vector>
using namespace std;

#include "Ogre.h"
#include "FlowSimHeaders.h"

namespace FlowSim
{
  class MyFlowSimGpu
{
public:
  MyFlowSimGpu();
  ~MyFlowSimGpu();
  void init(Ogre::SceneManager* sMgr,Ogre::RenderWindow* window);
  void update();

  void yaw(Ogre::Real x);
  void trust(Ogre::Real x);
  void addDRate(Ogre::Real dr);
  void fireMissile();
  void reset();
protected:
  
  //Simulation stuff//////////////////////////
  //simulation variables
  Ogre::Real _dt; //delta time
  int _dim; //simulation dimension
  int _numJacPrj; //num of iterations for Jacobi iteration for projection.
  int _numJacVis; //num of iterations for Jacobi iteration for Viscous diffusion of velocity.
  Ogre::Real _gSX; //grid scale X
  Ogre::Real _gSY; //grid scale y
  Ogre::Real _vis; //viscosity variable
  Ogre::Real _sgr; //grid resolution.
  Ogre::Real _dyeDRate; //dye diffusion rate
  Ogre::Vector4 _force0;
  Ogre::Vector4 _force1;
  Ogre::Vector4 _force2;
  Ogre::Real _thrust;
  //simulation fields
  Ogre::TexturePtr _vf; //velocity field texture
  Ogre::TexturePtr _tvf;//temporary velocity field texture
  Ogre::TexturePtr _divw; //divergence of vector field w.
  Ogre::TexturePtr _pref; //pressure field
  Ogre::TexturePtr _tpref; //temp pressure field.
  Ogre::TexturePtr _dyef;
  Ogre::TexturePtr _tdyef;
  Ogre::TexturePtr _vortf; //texture to do vorticity confinement.
  
  //Ogre Scene graph stuf////////////////////
  Ogre::Entity *_resultRenderEnt; //the node for our main render plane
  Ogre::Entity *_simPlaneEnt; //the simulation plane node (Full screen quad that triggers fragment program)
  
 
  //player stuff
  static const int MAX_MIS = 10;
  int _curMis;
  Ogre::SceneNode *_playerNode;
  Ogre::Entity* _playerEntity;
  Ogre::SceneNode *_missleNodes[10];
  Ogre::Entity* _missileEntities[10];
  Ogre::Real _misThrusts[10];
  bool _misVis[10];


  Ogre::Rectangle2D* _fullScreenQuad;
  Ogre::ManualObject* _semiFullScreenQuad;
  Ogre::SceneNode* _semiFullScreenNode;
  //Ogre::Rectangle2D* _semiFullScreenQuad; //semi full screen, used in projection frag programs.
  Ogre::Rectangle2D* _fS0;
  Ogre::Rectangle2D* _fS1;
  Ogre::Rectangle2D* _fS2;
  Ogre::Rectangle2D* _fS3;
  Ogre::Rectangle2D* _fS4;

  //Scene Nodes
  Ogre::SceneNode *_borderVelNode;
  Ogre::SceneNode *_borderPreNode;
  Ogre::SceneNode *_forceSourcesNode; //the node that cotains all force sources.
  Ogre::SceneNode *_dyeSourcesNode;
  //vector<auto_ptr<Ogre::Rectangle2D>> _forceSourcesVec;
  //Cameras
  Ogre::Camera* _cam1;
  //Scene stuff
 
  Ogre::SceneManager* _scnMgr;
  
  //METHODS///////////////////////////
  //simulation methods
  void advectVel(); //advect velocity
  void advectDye();
  void boundaryVel(); //set the boundary for velocity field.
  void boundaryPre(); //set the boundary for pressure field.
  void vorticityConf(); //voriticity confinement.
  void project(); //does the projection operator
  void divergenceVel(); //compute the divergence of velocity field.
  void jacobiPressure(); //jacobi iteration specifically related to the Projection operator.
  void computeDivFreeField(); //compute divergence free field, i.e u = w - grad p
  void viscousDiffuseVel(); //viscous diffusion for velocity
  void forceSources(); //sources of fource.
  void dyeSources();
  void swap(Ogre::TexturePtr &tx1, Ogre::TexturePtr &tx2);
  void zeroPressureField(); //zeros our pressure field
  void copyTextureToRTT(const Ogre::TexturePtr &tex, const Ogre::TexturePtr &rttTex);
  void renderGraphics();
 private:

  //initialization methods
  void createRenderTextures();
  void setupRender();
  void setupMaterials();
  void createForceSources();

  void debugOutBorders(const Ogre::TexturePtr tex); //debug output of border and inner cell values
  

  void createVelBorders(); //create velocity boundary manual objects
 
  void createSemiFullScreenQuad();
  void createPreBorders(); //create pressure boundary manual objects
 
  void setupRTT(const Ogre::TexturePtr &tex,Ogre::Camera *cam); //setup render to texture
  void loadBordersMaterialTexture(); //load default textures into border materials

  void createMissiles();
  void startMissilesVel();
  void startMissilesDye();
  void endMissiles();
  //void endMissilesDye();
  Ogre::SceneNode* _testManualNode;

};
}

#endif
